/*******************************************************************************
 * Copyright (c) 2006, 2009 Brad Reynolds and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Brad Reynolds - initial API and implementation
 *     Brad Reynolds - bug 116920, 159768
 *     Matthew Hall - bug 260329
 ******************************************************************************/
package org.eclipse.core.databinding.observable.gwt;

import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.ui.UIObject;
import com.google.gwt.user.client.ui.Widget;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import org.eclipse.core.databinding.Binding;
import org.eclipse.core.databinding.conversion.Converter;
import org.eclipse.core.databinding.conversion.gwt.Converters;
import org.eclipse.core.databinding.observable.IChangeListener;
import org.eclipse.core.databinding.observable.IObservableCollection;
import org.eclipse.core.databinding.observable.IStaleListener;
import org.eclipse.core.databinding.observable.Realm;
import org.eclipse.core.databinding.observable.value.ComputedValue;
import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.core.databinding.observable.value.IValueChangeListener;
import org.eclipse.core.runtime.IStatus;

/**
 * Based on Snippet004DataBindingContextErrorLabel.java
 * http://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet004DataBindingContextErrorLabel.java?view=markup
 */
public class ValidationStatusObservableValue implements IObservableValue{
  private IObservableValue implementation;
  private String errorStyleName;
  private String errorMessage;
  
  public ValidationStatusObservableValue(IObservableCollection bindings, String errorMessage, String errorStyleName) {
    this(Realm.getDefault(), bindings, errorMessage, errorStyleName);
  }

  public ValidationStatusObservableValue(Realm realm, final IObservableCollection bindings, String errorMessage, String errorStyleName) {
    implementation = new ComputedValue(realm, String.class) {
      protected Object calculate() {
        return getStatusMessages(bindings);
      }
    };
    this.errorMessage = errorMessage;
    this.errorStyleName = errorStyleName;
  }

  public void addChangeListener(IChangeListener listener) {
    implementation.addChangeListener(listener);
  }

  public void addStaleListener(IStaleListener listener) {
    implementation.addStaleListener(listener);
  }

  public void addValueChangeListener(IValueChangeListener listener) {
    implementation.addValueChangeListener(listener);
  }

  public void dispose() {
    implementation.dispose();
  }

  public Realm getRealm() {
    return implementation.getRealm();
  }

  public Object getValue() {
    return implementation.getValue();
  }

  public Object getValueType() {
    return implementation.getValueType();
  }

  public boolean isStale() {
    return implementation.isStale();
  }

  public void removeChangeListener(IChangeListener listener) {
    implementation.removeChangeListener(listener);
  }

  public void removeStaleListener(IStaleListener listener) {
    implementation.removeStaleListener(listener);
  }

  public void removeValueChangeListener(IValueChangeListener listener) {
    implementation.removeValueChangeListener(listener);
  }

  public void setValue(Object value) {
    implementation.setValue(value);
  }

  private String getStatusMessages(Collection bindings){
    String ret = "";
    int nError = 0;

    // list of 'error' widget
    List<Widget> errorWidgets = new ArrayList<Widget>();
    for (Iterator it = bindings.iterator(); it.hasNext();) {
      Binding binding = (Binding) it.next();
      IStatus status = (IStatus)binding.getValidationStatus().getValue();
      UIObject widget = null;
      if (binding.getTarget() instanceof IWidgetObservable){
        widget = ((IWidgetObservable)binding.getTarget()).getWidget();
      }
      if (widget != null){
        if (!status.isOK() && (widget instanceof Widget)) {
          errorWidgets.add((Widget)widget);
        }
      }
    }
    
    for (Iterator it = bindings.iterator(); it.hasNext();) {
      Binding binding = (Binding) it.next();
      if (binding.getModel() == this){
        continue;
      }
      
      IStatus status = (IStatus)binding.getValidationStatus().getValue();
      UIObject widget = null;
      if (binding.getTarget() instanceof IWidgetObservable){
        widget = ((IWidgetObservable)binding.getTarget()).getWidget();
      }
      if (widget != null){
        if (!status.isOK()) {
          String errorText;
          if (status.getException() != null){
            status.getException().printStackTrace();
            // ret += exception2Text(status.getException()) + "<p>";
            errorText = exception2Text(status.getException());
          }else{
            System.out.println("ERROR: ValidationStatus: " + status.getMessage());
            // ret += status.getMessage() + "<p>";
            errorText = status.getMessage();
          }
          nError++;
          ret += errorText;
          
          widget.addStyleName(errorStyleName);
          widget.setTitle(errorText);
        }else{
          if (!errorWidgets.contains(widget)){
            widget.removeStyleName(errorStyleName);
            widget.setTitle(DOM.getElementProperty(widget.getElement(), "tooltip"));
          }
        }
      }
      // updateWidget(binding);
    }
    
    if (nError > 1){
      return errorMessage;
    }

    return ret;
  }

  private String exception2Text(Throwable error){
    if (error == null){
      return "";
    }
    Converter converter = Converters.getConverter(error.getClass(), String.class);
    if (converter == null){
      System.out.println("WARN: No converter for " + error.getClass().getName());
      return error.getMessage();
    }else{
      return (String)converter.convert(error);
    }
  }
}
